package redis_service

import (
	"github.com/go-redis/redis"
	"gopkg/app"
	"gopkg/app/interfaces"
	"gopkg/util/action-queue"
	"time"
)

func NewRedisService() interfaces.Service {
	return &redisService{
		clients:     map[string]*redis.Client{},
		actionQueue: action_queue.NewActionQueue(),
	}
}

type redisService struct {
	clients     map[string]*redis.Client
	actionQueue action_queue.ActionQueue
}

func (r *redisService) Name() string {
	return "RedisService"
}

func (r *redisService) Awake() {
	conf := map[string]struct {
		//for conn
		Network  string
		Addr     string
		Password string
		DB       int

		DialTimeout  int
		ReadTimeout  int
		WriteTimeout int
		//for pool
		PoolSize int
	}{}

	err := app.App().Config().Get("RedisService").Scan(&conf)
	if err != nil {
		log.Panicf("read redis service config %s", err)
	}

	for k, v := range conf {
		client := redis.NewClient(&redis.Options{
			Network:      v.Network,
			Addr:         v.Addr,
			Password:     v.Password,
			DB:           v.DB,
			MaxRetries:   1,
			DialTimeout:  time.Duration(v.DialTimeout) * time.Second,
			ReadTimeout:  time.Duration(v.ReadTimeout) * time.Second,
			WriteTimeout: time.Duration(v.WriteTimeout) * time.Second,
			PoolSize:     v.PoolSize,
		})

		val, err := client.Ping().Result()
		if err != nil || val != "PONG" {
			log.Panicf("redis %s %+v init failed", k, v)
		}
		r.clients[k] = client
	}
	r.actionQueue.EnableCapturePanic(true)
	r.actionQueue.StartLoop()
}

func (r *redisService) Shut() {
	r.actionQueue.StopLoop()
	r.actionQueue.Wait()
	for _, c := range r.clients {
		c.Close()
	}
}

func (r *redisService) SyncGetClient(name string) redis.UniversalClient {
	return r.clients[name]
}

func (r *redisService) AsyncGetClient(name string, op func(client redis.UniversalClient) (interface{}, error), opCb func(result interface{}, err error)) {
	r.actionQueue.Post(func() {
		result, err := op(r.SyncGetClient(name))
		app.App().PostAction(func() {
			opCb(result, err)
		})
	})
}
